
package com.example.erp.log;

import com.example.erp.entity.UserInfo;
import com.example.erp.exception.ErpErrorCode;
import com.example.erp.exception.ErpException;
import com.example.erp.mapper.UserInfoMapper;
import com.example.erp.utils.JwtHelper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 获取用户基本信息
 * 通过token获取用户基本信息,每个线程对应一个
 *
 * @author gxz
 */
@Aspect
@Component
@Order(2)
public class ErpRequestAspect {
    private static Logger logger = LoggerFactory.getLogger(ErpRequestAspect.class);

    @Resource
    private UserInfoMapper userInfoMapper;

    @Autowired
    private JwtHelper jwtHelper;

    /**
     * 拦截所有Controller类中所有public方法
     */
    @Pointcut("(@target(org.springframework.web.bind.annotation.RestController)) && (execution(public * com.example.erp..*.*(..)))")
    public void executionService() {
    }

    /**
     * 方法调用之前调用
     */
    @Before("executionService()")
    public void doBefore(JoinPoint joinPoint) {
        // 初始化用户信息类
        ErpUserContext.init();
        // 取出请求参数
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 若请求参数为空,方法结束
        if (requestAttributes == null) {
            logger.info("ServletRequestAttributes is null");
            return;
        }
        // 根据注解设置判断是否需要Token，默认为需要
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();
        TokenRequired tokenRequired = targetMethod.getAnnotation(TokenRequired.class);
        if (!ObjectUtils.isEmpty(tokenRequired) && !tokenRequired.value()) {
            return;
        }
        // 取出请求request
        HttpServletRequest request = requestAttributes.getRequest();
        // token
        String token = "";
        // 取出cookies
        Cookie[] cookies = request.getCookies();
        // 若cookies不为空,取出X_TOKEN
        if (ArrayUtils.isNotEmpty(cookies)) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(ErpUserContext.X_TOKEN)) {
                    token = cookie.getValue();
                    break;
                }
            }
        }
        // 若cookies中X_TOKEN为空,则从request中取出X_TOKEN
        if (StringUtils.isEmpty(token)) {
            token = request.getHeader(ErpUserContext.X_TOKEN);
        }
        logger.info("request URI={}, token:{}", request.getRequestURI(), token);
        // 若token为空,抛出异常
        if (StringUtils.isNotEmpty(token)) {
            ErpUserContext.putToken(token);
        } else {
            throw new ErpException(ErpErrorCode.TOKEN_NOT_NULL.getErrorCode(), ErpErrorCode.TOKEN_NOT_NULL.getErrorReason());
        }
        Map<String, Object> tokenObj = null;
        try {
            tokenObj = jwtHelper.validateTokenAndGetClaims(request);
        } catch (Exception e) {
            throw new ErpException(ErpErrorCode.TOKEN_FAILED.getErrorCode(), ErpErrorCode.TOKEN_FAILED.getErrorReason());
        }
        Integer id = (Integer) tokenObj.get("id");
        // token不为空,调用组织接口查询用户信息
        UserInfo userInfo = userInfoMapper.selectById(id);

        //调用组织失败或者返回数据为空，直接返回
        if (userInfo == null) {
            throw new ErpException(ErpErrorCode.USER_MESSAGE_MISS.getErrorCode(), "获取用户登录信息失败");
        }
        // 取出userName,不为空则记录用到用户信息类
        String userName = userInfo.getUserName();
        if (StringUtils.isNotEmpty(userName)) {
            ErpUserContext.putUserName(userName);
        }
        // 取出userId,不为空则记录用到用户信息类
        String userId = userInfo.getId();
        if (StringUtils.isNotEmpty(userId)) {
            ErpUserContext.putUserId(userId);
        }
        // 取出mobile,不为空则记录用到用户信息类
        String mobile = userInfo.getMobilePhoneNo();
        if (StringUtils.isNotEmpty(mobile)) {
            ErpUserContext.putMobile(mobile);
        }
        // 登录名loginName,不为空则记录用到用户信息类
        String loginName = userInfo.getLoginName();
        if (StringUtils.isNotEmpty(loginName)) {
            ErpUserContext.putLoginName(loginName);
        }
        // 把用户登录信息放到ThreadLocal中
        ErpUserContext.putUserInfo(userInfo);
    }


    /**
     * 方法之后调用 清楚线程缓存
     */
    @AfterReturning(pointcut = "executionService()")
    public void doAfterReturning() {
        logger.info("clear cache for user login info");
        ErpUserContext.remove();
    }

    /**
     * 方法之后调用耗时打印
     */
    @Around(value = "executionService()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result;
        StringBuilder sbLog = new StringBuilder();
        try {
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            sbLog.append(String.format("method=%s", methodSignature.getMethod().getName()));
            long startTime = System.currentTimeMillis();
            result = proceedingJoinPoint.proceed();
            long endTime = System.currentTimeMillis();
            sbLog.append(String.format(",cost time=%sms", endTime - startTime));
        } finally {
            logger.info(sbLog.toString());
        }
        return result;
    }
}